package edu.uj.petri.view;

import edu.uj.petri.commands.Memento;
import java.awt.Color;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.util.LinkedList;
import java.util.List;

public class ArcSegment extends DiagramElement {
    
    private LinkingPoint begin;
    private LinkingPoint end;
    private NodeView beginNode;
    private NodeView endNode;
    private double x1, x2, y1, y2;
    
    // jak blisko trzeba kliknac, aby zaznaczyc
    private final static double selectDist = 10.0;
    
    public ArcSegment(LinkingPoint begin, LinkingPoint end) {
        
        this.begin = begin;
        this.end = end;
    }
    
    public ArcSegment(NodeView begin, LinkingPoint end) {
        
        this.beginNode = begin;
        this.end = end;
    }
    
    public ArcSegment(LinkingPoint begin, NodeView end) {
        
        this.begin = begin;
        this.endNode = end;
    }
    
    public ArcSegment(NodeView begin, NodeView end) {
        
        this.beginNode = begin;
        this.endNode = end;
    }
    
    public boolean containsPoint(Point2D point) {
        
        return Line2D.ptSegDist(x1, y1, x2, y2, point.getX(), point.getY()) < selectDist;
    }
    
    public boolean crossesRectangle(Rectangle2D rect) {
        return false;
    }
    
    /**
     * draw a segment
     */
    public double getX1() {
        if (beginNode != null) return beginNode.position.getX();
        else return begin.position.getX();
    }
    
    public double getX2() {
        if (endNode != null) return endNode.position.getX();
        else return end.position.getX();
    }
    
    public double getY1() {
        if (beginNode != null) return beginNode.position.getY();
        else return begin.position.getY();
    }
    
    public double getY2() {
        if (endNode != null) return endNode.position.getY();
        else return end.position.getY();
    }
    
    public void draw(Canvas canvas) {
        
        if (beginNode != null) {
            x1 = beginNode.position.getX();
            y1 = beginNode.position.getY();
        } else {
            x1 = begin.position.getX();
            y1 = begin.position.getY();
        }
        
        if (endNode != null) {
            x2 = endNode.position.getX();
            y2 = endNode.position.getY();
        } else {
            x2 = end.position.getX();
            y2 = end.position.getY();
        }
        
        if (selected) canvas.drawLine(x1, y1, x2, y2, SELECTED_COLOR);
        else canvas.drawLine(x1, y1, x2, y2, UNSELECTED_COLOR);
    }
    
    
    public void drawCircle(Canvas canvas) {
        draw( canvas );
        
        double dx = getX2() - getX1();
        double dy = getY2() - getY1();
        
        Point2D direction = new Point2D.Double( 1.2*dx/Math.sqrt(dx*dx+dy*dy), 1.2*dy/Math.sqrt(dx*dx+dy*dy));
        
        Rectangle2D rect = new Rectangle2D.Double(
                getX1() + direction.getX()*PlaceView.radius - 5,
                getY1() + direction.getY()*PlaceView.radius - 5,
                10, 10);
        
        canvas.drawCircle( rect, Color.BLACK, false );
    }
    
    public static void drawLineWithCircle( double x1, double y1, double x2, double y2, Canvas canvas, Color color ) {
        canvas.drawLine( x1, y1, x2, y2, color );
        
        double dx = x2 - x1;
        double dy = y2 - y1;
        
        Point2D direction = new Point2D.Double( 1.2*dx/Math.sqrt(dx*dx+dy*dy), 1.2*dy/Math.sqrt(dx*dx+dy*dy));
        
        Rectangle2D rect = new Rectangle2D.Double(
                x1 + direction.getX()*PlaceView.radius - 5,
                y1 + direction.getY()*PlaceView.radius - 5,
                10, 10);
        
        canvas.drawCircle( rect, Color.BLACK, false );
    }
    
    public void drawArrow(Canvas canvas, Color color) {
        draw(canvas);
        drawArrow( getX1(), getY1(), getX2(), getY2(), canvas, color, getNodeEnd() );
    }
    
    private static final double arrowAngle = 0.3;
    private static final double arrowSize = 15;
    
    
    static public void drawArrow(  double x1, double y1, double x2, double y2, Canvas canvas, Color color, NodeView node ) {
        canvas.drawLine( x1, y1, x2, y2, color );
        
        double dx = x1 - x2;
        double dy = y1 - y2;
        
        if ((Math.abs(dx) < arrowSize) && (Math.abs(dy) < arrowSize)) return;
        
        Point2D direction = new Point2D.Double( 1.2*dx/Math.sqrt(dx*dx+dy*dy), 1.2*dy/Math.sqrt(dx*dx+dy*dy));
        
        Point2D top = null;
        
        if (node instanceof PlaceView) {
            top = new Point2D.Double( x2+0.9*direction.getX()*PlaceView.radius, y2+0.9*direction.getY()*PlaceView.radius ) ;
        } else {
            top = new Point2D.Double( x2, y2) ;
        }
        
        AffineTransform rotate1 = AffineTransform.getRotateInstance( arrowAngle, top.getX(), top.getY());
        AffineTransform rotate2 = AffineTransform.getRotateInstance( -arrowAngle, top.getX(), top.getY());
        
        Point2D rotatedPoint1 = null, rotatedPoint2 = null;
        
        rotatedPoint1 = rotate1.transform(new Point2D.Double( top.getX()+direction.getX()*arrowSize, top.getY()+direction.getY()*arrowSize), rotatedPoint1 );
        rotatedPoint2 = rotate2.transform(new Point2D.Double( top.getX()+direction.getX()*arrowSize, top.getY()+direction.getY()*arrowSize), rotatedPoint2 );
        
        List<Point2D> points = new LinkedList<Point2D>();
        
        points.add( top );
        points.add( rotatedPoint1 );
        points.add( rotatedPoint2 );
        
        canvas.drawPolygon( points );
    }
    
    /**
     * return false
     */
    public boolean canBeMoved(Point2D startPoint) {
        return false;
    }
    
    /**
     * throws exception
     */
    public void move(Point2D start, Point2D end) {
        throw new IllegalArgumentException("arc segment cannot be moved");
    }
    
    /**
     * add linking point
     */
    public void doubleClick( Point2D point ) {
    }
    
    public ContextMenu getContextMenu() {
        return null;
    }
    
    public void removeFrom(View view) {
    }
    
    private static final long serialVersionUID = -2358407491576756267L;
    
    public LinkingPoint getBegin() {
        return begin;
    }
    
    public NodeView getNodeBegin() {
        return beginNode;
    }
    
    public void setBegin(LinkingPoint begin) {
        this.beginNode = null;
        this.begin = begin;
    }
    
    public LinkingPoint getEnd() {
        
        return end;
    }
    
    public NodeView getNodeEnd() {
        return endNode;
    }
    
    public void setEnd(LinkingPoint end) {
        this.endNode = null;
        this.end = end;
    }
    
    public void setNodeEnd(NodeView node) {
        this.endNode = node;
        this.end = null;
    }
    
    public Memento getMemento() {
        return null;
    }
    
    public void applyMemento(Memento memento) {
        // do nothing
    }
}
